#include<Python.h>
#include"structmember.h"
#include"datetime.h"

#include"Decoder.h"
#include<mad.h>

static enum mad_flow mad_input(void *, struct mad_stream *);
static inline signed int scale(mad_fixed_t);
static enum mad_flow mad_output(void *, struct mad_header const *, struct mad_pcm *);
static enum mad_flow mad_header(void *, struct mad_header const *);
static enum mad_flow mad_error(void *, struct mad_stream *, struct mad_frame *);

static enum mad_flow long2flow(long);

static void
Decoder_dealloc(Decoder *self)
{
	if (!self->isclosed)
		mad_decoder_finish(&self->decoder);
	PyMem_Free(self->buff);
	PyMem_Free(self->buff_out);
	Py_XDECREF(self->FileObject);
	Py_XDECREF(self->object);
	Py_XDECREF(self->outputFunc);
	Py_XDECREF(self->headerFunc);
	Py_XDECREF(self->errorFunc);
	Py_TYPE(self)->tp_free((PyObject*)self);
	//fprintf(stderr, "Decoder deallocated\n");
}

static PyObject *
Decoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	Decoder *self;

	self = (Decoder*) type->tp_alloc(type, 0);
	if (self != NULL) {
		const int bufsiz = 4098;

		/* allocating data space.  */
		self->buff = PyMem_Malloc(bufsiz);
		self->buff_out = PyMem_Malloc(bufsiz);
		if ( self->buff==NULL || self->buff_out==NULL ) {
			return PyErr_NoMemory();
		}
		self->bufsiz = self->bufsiz_out = bufsiz;
	}

	self->isclosed = 0;
	return (PyObject*) self;
}

static int
Decoder_init(Decoder *self, PyObject *args, PyObject *kwds)
{
	PyObject *file=NULL, *object=NULL;
	void *tmp;

	static char *kwlist[] = {"file", "object", "bufsiz", NULL};
	int bufsiz = 4098;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi", kwlist,
				&file, &object, &bufsiz)) 
		return -1;

	if (file) {
		tmp = self->FileObject;
		Py_INCREF(file);
		self->FileObject = file;
		Py_XDECREF(tmp);
	}
	if (object) {
		tmp = self->object;
		Py_INCREF(object);
		self->object = object;
		Py_XDECREF(tmp);
	}

	PyDateTime_IMPORT;

	Py_XDECREF(self->outputFunc);
	//self->outputFunc = outputFuncDefault;
	Py_XDECREF(self->headerFunc);
	//self->headerFunc = headerFuncDefault;
	Py_XDECREF(self->errorFunc);
	//self->errorFunc = errorFuncDefault;

	/* allocating data space.  */
	if (self->buff) PyMem_Free(self->buff);
	if (self->buff_out) PyMem_Free(self->buff_out);
	self->buff = PyMem_Malloc(bufsiz);
	self->buff_out = PyMem_Malloc(bufsiz);
	if (self->buff==NULL || self->buff_out==NULL) {
		return -1;
	}

	self->bufsiz = self->bufsiz_out = bufsiz;
	self->isclosed = 0;
	return 0;
}

static PyObject *
Decoder_config(Decoder *self, PyObject *args, PyObject *kwds)
{
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
Decoder_run(Decoder *self, PyObject *args, PyObject *kwds)
{
	int result;
	static char *kwlist[] = {"header", "output", "error", NULL};
	PyObject *header, *output, *error;
	header=output=error=NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist,
				&header, &output, &error)) 
		return NULL;

	if (header) {
		Py_INCREF(header);
		Py_XDECREF(self->headerFunc);
		self->headerFunc = header;
	}
	if (output) {
		Py_INCREF(output);
		Py_XDECREF(self->outputFunc);
		self->outputFunc = output;
	}
	if (error) {
		Py_INCREF(error);
		Py_XDECREF(self->errorFunc);
		self->errorFunc = error;
	}

	/* open new decoder.  */
	mad_decoder_init(&self->decoder, self,
			mad_input, mad_header, 0 /* filter */, mad_output,
			mad_error, 0 /* message */);

	/* start decoding.  */
	result = mad_decoder_run(&self->decoder, MAD_DECODER_MODE_SYNC);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
Decoder_finish(Decoder *self)
{
	if (!self->isclosed) {
		mad_decoder_finish(&self->decoder);
		self->isclosed = 1;
	}
	Py_INCREF(Py_None);
	return Py_None;
}
static
enum mad_flow mad_input(void *arg,
		    struct mad_stream *stream)
{
	Decoder *self = (Decoder*)arg;
	PyObject *byteObj;
	char *data;
	int datasize;
	int remaining_bytes = 0;
	//struct buffer *buffer = data;

	/* Reserve some data which have not been processed yet.  */
	if (stream->next_frame) {
		//printf("next_frame=%p, stream->bufend=%p\n", stream->next_frame, stream->bufend);
		remaining_bytes = stream->bufend - stream->next_frame;
		if (remaining_bytes > self->bufsiz || remaining_bytes<=0) {
			//printf("mad_input: error too large remaining_bytes");
			//printf("%d bytes remianing!\n", remaining_bytes);
			return MAD_FLOW_STOP;
		}
		memmove(self->buff, stream->next_frame, remaining_bytes);
	}

	/* Read data from FileObject through Python Method.  */
	byteObj = PyObject_CallMethod(self->FileObject, "read", "i", self->bufsiz - remaining_bytes);
	if (-1==PyObject_AsReadBuffer(byteObj, (const void**)&data, &datasize)) {
		//printf("mad_input: error FLOW_STOP\n");
		return MAD_FLOW_STOP;
	}
	if (datasize==0) {
		//printf("mad_input: FLOW_STOP\n");
		return MAD_FLOW_STOP;
	}

	/* Copy these data to the buffer.  */
	memmove(self->buff+remaining_bytes, data, datasize);
	/* Set buffer.  */
	mad_stream_buffer(stream, (unsigned char const*)self->buff, remaining_bytes + datasize);

	Py_DECREF(byteObj);
	return MAD_FLOW_CONTINUE;
}

static
enum mad_flow mad_header(void *args, struct mad_header const *header)
{
	Decoder *self = (Decoder*)args;
	PyObject *time, *arglist, *result;
	long rtn;
	/*
	int hour,minute,second,usecond;
	second = header->duration.seconds;
	hour   = (int) (second/3600);
	second = second%3600;
	minute = (int) (second/60);
	second = second%60;
	usecond = (float)header->duration.fraction/(float)MAD_TIMER_RESOLUTION * 1000*1000;

	time = PyTime_FromTime(0,0, second, usecond);
	//time = PyTime_FromTime(0,0, 0, 0);
	*/
	int sec, usec;
	sec = header->duration.seconds;
	usec= (float)header->duration.fraction/(float)MAD_TIMER_RESOLUTION * 1000*1000;
	time = PyDelta_FromDSU(0, sec, usec);
	if (!time) return MAD_FLOW_BREAK;


	arglist = Py_BuildValue("(OO)", self->object, time );
	result = PyEval_CallObject(self->headerFunc, arglist);
	Py_DECREF(arglist);
	Py_DECREF(time);
	if (result==NULL) return MAD_FLOW_BREAK;

	rtn = PyLong_AsLong(result);
	Py_DECREF(result);

	return long2flow(rtn);
}

static inline
// TODO: ここは書き直さないとライセンス違反かも
signed int scale(mad_fixed_t sample)
{
	/* round */
	sample += (1L << (MAD_F_FRACBITS - 16));

	/* clip */
	if (sample >= MAD_F_ONE)
		sample = MAD_F_ONE - 1;
	else if (sample < -MAD_F_ONE)
		sample = -MAD_F_ONE;

	/* quantize */
	return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static
enum mad_flow mad_output(void *data,
                     struct mad_header const *header,
                     struct mad_pcm *pcm)
{
	PyObject *arglist, *result;
	Decoder *self = (Decoder*)data;
	unsigned int nchannels, nsamples;
	mad_fixed_t const *left_ch, *right_ch;
	int i=0;
	long rtn;

	nchannels = pcm->channels;
	nsamples  = pcm->length;
	left_ch   = pcm->samples[0];
	right_ch  = pcm->samples[1];

	if (nsamples*4 > self->bufsiz_out) {
		PyMem_Free(self->buff_out);
		self->buff_out = PyMem_Malloc( nsamples*4 );
		if (!self->buff_out) return MAD_FLOW_STOP;
		self->bufsiz_out = nsamples*4;
	}

	while ( nsamples-->0 ) {

		signed int sample;

		/* output sample(s) in 16-bit signed little-endian PCM */
		sample = scale(*left_ch++);
		self->buff_out[i++] = ((sample >> 0) & 0xff);
		self->buff_out[i++] = ((sample >> 8) & 0xff);

		// TODO
		sample = scale(*right_ch++);
		self->buff_out[i++] = ((sample >> 0) & 0xff);
		self->buff_out[i++] = ((sample >> 8) & 0xff);

	}

	arglist = Py_BuildValue("(Oy#)", self->object, self->buff_out, i);
	result = PyEval_CallObject(self->outputFunc, arglist);
	Py_DECREF(arglist);
	if (result==NULL)
		return MAD_FLOW_BREAK;

	rtn = PyLong_AsLong(result);
	Py_DECREF(result);

	return long2flow(rtn);

}

static
enum mad_flow long2flow(long val)
{
	if ( val == MAD_FLOW_STOP
		|| val == MAD_FLOW_CONTINUE
		|| val == MAD_FLOW_BREAK
		|| val == MAD_FLOW_IGNORE )
		return (enum mad_flow) val;
	else
		return MAD_FLOW_BREAK;
}

static
enum mad_flow mad_error(void *data,
		struct mad_stream *stream,
		struct mad_frame *frame)
{
	/*
	fprintf(stderr, "mad_error: 0x%04x (%s) at byte offset %p\n",
			stream->error, mad_stream_errorstr(stream), stream->this_frame);

	fprintf(stderr, "mad_stream {\n"
			"\tbuffer %p\n"
			"\tskiplen %lu\n"
			"\tthis_frame %p\n"
			"\tnext_frame %p\n"
			"\terror %u\n"
			"}\n",
		   stream->buffer, stream->skiplen, stream->this_frame, stream->next_frame, stream->error);
		   */

	/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
	//return MAD_FLOW_BREAK;
	return MAD_FLOW_CONTINUE;
}


PyMemberDef Decoder_members[] = {
	//{"first", T_OBJECT_EX, offsetof(Noddy,first), 0, "first name" },
	//{"last", T_OBJECT_EX, offsetof(Noddy,last), 0, "last name" },
	//{"number", T_INT, offsetof(Noddy, number), 0, "noddy number" },
	{"number", T_INT, offsetof(Decoder, bufsiz), 0, "noddy number" },
	{NULL}
};
PyMethodDef Decoder_methods[] = {
	{"run", (PyCFunction)Decoder_run, METH_VARARGS|METH_KEYWORDS,
		"Start decoding on another thread." },
	{"finish", (PyCFunction)Decoder_finish, METH_NOARGS,
		"finish decoding." },
	{"config", (PyCFunction)Decoder_config, METH_NOARGS,
		"Start decoding on another thread." },
	{NULL}
};

PyTypeObject DecoderType = {
	//PyObject_HEAD_INIT(NULL) // Documentにはこれと書いてあるけど、多分違う
	PyVarObject_HEAD_INIT(NULL, 0)
	"sotetsu.madsimple.Decoder",
	sizeof(Decoder),
	0,
	(destructor)Decoder_dealloc,
    0,                         /* tp_print */
    0,                         /* tp_getattr */
    0,                         /* tp_setattr */
    0,                         /* tp_reserved */
    0,                         /* tp_repr */
    0,                         /* tp_as_number */
    0,                         /* tp_as_sequence */
    0,                         /* tp_as_mapping */
    0,                         /* tp_hash  */
    0,                         /* tp_call */
    0,                         /* tp_str */
    0,                         /* tp_getattro */
    0,                         /* tp_setattro */
    0,                         /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT |
        Py_TPFLAGS_BASETYPE,   /* tp_flags */
    "madsimple's decoder objects",           /* tp_doc */
    0,		               /* tp_traverse */
    0,		               /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    Decoder_methods,             /* tp_methods */
    Decoder_members,             /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)Decoder_init,      /* tp_init */
    0,                         /* tp_alloc */
    Decoder_new,                 /* tp_new */
};

